pesto: target address mapping and netavark integration#1004
Conversation
c17020c to
5b996c5
Compare
5b996c5 to
1b2918e
Compare
| logrus.Errorf("pesto: reading addr state: %v", err) | ||
| return |
There was a problem hiding this comment.
I feel like it would be better if return an actual error in this function and then in the end just let the caller log it once, that way we can skip the pesto prefix from each log here and just have the prefix once on the caller side.
| // firstIPsWithNetwork picks the first IPv4 and IPv6 from the network IPs | ||
| // map (sorted by network name), returning the IP and the owning network. | ||
| func firstIPsWithNetwork(netIPs map[string]pestoNetworkIPs) (ipv4, ipv4Net, ipv6, ipv6Net string) { | ||
| for _, name := range sortedNetworkNames(netIPs) { |
There was a problem hiding this comment.
we have a network order more or less given via the input network that are sorted I think we should respect that order and not sort based on the names.
| // pestoAddrState tracks which container IPs are currently used for pesto | ||
| // port forwarding. Stored as a per-container JSON file in the rootless | ||
| // netns directory so connect/disconnect operations can detect whether | ||
| // ports are already published and remap when necessary. | ||
| type pestoAddrState struct { |
There was a problem hiding this comment.
so I am thinking if we can avoid yet another extra way to keep the state here via the josn file, the podman db already carries the network result as state so I feel like adding onto that seems better as we then only store it in one location.
Of course it means we then need to pass the state from the podman db back into the teardown/setup functions but I feel like this could be a bit cleaner?
cc @mheon
| if state == nil { | ||
| // Migration fallback: no state file means this container was set up | ||
| // before state tracking existed. Best-effort delete using IPAM IPs. | ||
| ipv4, ipv6 := containerIPsFromNetOpts(opts) |
There was a problem hiding this comment.
I don't think this will gain us anything? The port format has changed so pasta would not consider them to be the same I think?
I guess there is not much do be done, we marked it as experimental so I am fine telling users they need to stop all containers after the update and only then start the new one which should kill and restart pasta to clean out the stale ports. (OR well just let them reboot)
There was a problem hiding this comment.
Yes, that is leftover after my mistake. I will drop that.
| // In pesto mode, pasta handles the full forwarding path including target | ||
| // address mapping, so netavark doesn't need DNAT rules at all. Clear port | ||
| // mappings from the payload to avoid unnecessary iptables/nftables rules. | ||
| if n.rootlessPortForwarder == config.RootlessPortForwarderPasta && n.networkRootless { | ||
| opts.PortMappings = nil |
There was a problem hiding this comment.
I would not drop the ports here, a user could still use podman unshare --rootless-netns curl 127.0.0.1:80 for example and that shoudl work. With nftables we batch update all rules anyway so this will not gain us anything noticeably
| strings.Contains(string(out), "unrecognized option '"+mapGuestAddrOpt) && | ||
| len(mapGuestAddrIPs) == 1 && mapGuestAddrIPs[0] == mapGuestAddrIpv4 { | ||
| // we did add the default --map-guest-addr option, if users set something different we want | ||
| len(mapGuestAddrIPs) == 2 && mapGuestAddrIPs[0] == mapGuestAddrIpv4 && mapGuestAddrIPs[1] == mapGuestAddrIpv6 { |
There was a problem hiding this comment.
I think pasta has supported --map-guest-addr for long enough now that we can hard depend on it. I would first simplify this by removing the retry code path. And then we do not need to special case this new argument here
abc89b3 to
8aa86af
Compare
Fixes: podman-container-tools/podman#28770 Signed-off-by: Jan Rodák <hony.com@seznam.cz>
8aa86af to
c7b86fe
Compare
|
PTAL @Luap99 |
| // When pasta restarts all forwarding rules are lost. | ||
| // pestoSetup detects this via pesto --show and re-publishes | ||
| // on the next Setup call. |
There was a problem hiding this comment.
That doesn't seem to be true, I guess there is nothing to be done here. I would just drop this comment.
|
|
||
| // config is the containers.conf config, needed for FindHelperBinary | ||
| // when invoking pesto for dynamic port forwarding. | ||
| config *config.Config |
There was a problem hiding this comment.
I missed this is the first round of review but so far I tried to keep the full config out of the network interface as we lookup the netavark path once at the beginning and then store it as string instead.
I guess the is not strict reason to not have it so I am fine to add this here but it would allow us some cleanup/removal of fields above in theory. (anyhow lets not make this PR more complicated I guess, we can do that as some cleanup in the future)
| opts := options.NetworkOptions | ||
| if opts.NetworkStatus == nil { | ||
| opts.NetworkStatus = result | ||
| } | ||
| if len(opts.NetworkOrder) == 0 { | ||
| opts.NetworkOrder = make([]string, 0, len(opts.Networks)) | ||
| for _, net := range opts.Networks { | ||
| opts.NetworkOrder = append(opts.NetworkOrder, net.Name) | ||
| } | ||
| } | ||
| if err := n.pestoSetup(opts); err != nil { | ||
| return nil, err | ||
| } |
There was a problem hiding this comment.
there is nothing here to prevent a double add?
$ bin/podman run --name c1 --network net1 -p 8080:8080 -d quay.io/libpod/testimage:20241011 sleep 1000
447eea8c3b4e8bb9ac2a35d4f03f34502d59757e95eb3029db9384b5286f42bd
$ bin/podman network connect net2 c1
Error: pesto failed: exit status 1
output: Forwarding configuration conflict: TCP [0.0.0.0]:8080 => 10.89.2.5:8080 versus TCP [0.0.0.0]:8080 => 10.89.2.5:8080
Unable to add rule TCP [0.0.0.0]:8080 => 10.89.2.5:8080
If the network status is not nil we should assume the setup already happened on a prior network and do nothing
| mapGuestAddrIpv6 = "fc00::1" | ||
|
|
||
| // guestAddrIpv6 is assigned to the guest interface so the IPv6 gateway is reachable. | ||
| guestAddrIpv6 = "fc00::2" | ||
|
|
||
| // gatewayIpv6 is the IPv6 default gateway for the guest. pasta uses this as the source | ||
| // address for inbound IPv6 forwarding. Must differ from mapGuestAddrIpv6 to avoid | ||
| // --map-guest-addr intercepting reply traffic. | ||
| // See: https://bugs.passt.top/show_bug.cgi?id=217 | ||
| gatewayIpv6 = "fc00::3" |
There was a problem hiding this comment.
I would prefer consistency with v4 that
mapGuestAddrIpv6 = fc00::2
then
gatewayIpv6 = fc00::1 (using first ip as gateway feels more natural)
guestAddrIpv6 = fc00::3
| cmdArgs = append(cmdArgs, mapGuestAddrOpt, mapGuestAddrIpv4, mapGuestAddrOpt, mapGuestAddrIpv6) | ||
| mapGuestAddrIPs = append(mapGuestAddrIPs, mapGuestAddrIpv4, mapGuestAddrIpv6) | ||
| } | ||
|
|
||
| // Pass --address and --gateway with non-link-local IPv6 addresses so pasta | ||
| // uses a routable source for inbound IPv6 forwarding (instead of link-local | ||
| // which is not routable across bridges). --address is required so the kernel | ||
| // can add the route to the gateway. | ||
| // See: https://bugs.passt.top/show_bug.cgi?id=217 | ||
| hasIPv6Gateway := false | ||
| for _, ip := range gatewayIPs { | ||
| if strings.Contains(ip, ":") { | ||
| hasIPv6Gateway = true | ||
| break | ||
| } | ||
| } | ||
| if !hasIPv6Gateway { | ||
| hasIPv6Addr := false | ||
| for _, ip := range addressIPs { | ||
| if strings.Contains(ip, ":") { | ||
| hasIPv6Addr = true | ||
| break | ||
| } | ||
| } | ||
| if !hasIPv6Addr { | ||
| cmdArgs = append(cmdArgs, addressOpt, guestAddrIpv6) | ||
| } | ||
| cmdArgs = append(cmdArgs, gatewayOpt, gatewayIpv6) | ||
| } | ||
|
|
||
| cmdArgs = append(cmdArgs, "--netns", opts.Netns) |
There was a problem hiding this comment.
I would try messing with ipv6 to much on the normal code path (the main --network pasta) hard coding the address there should not be needed
IMO the better thing isd move the extra options into the rootless netns setupPasta, i.e. where add the pid arg.
I think we should be fine hard coding the three options there. Though we would need to hard the v4 mapGuestAddrIpv4 there then as well.
| NetworkStatus map[string]StatusBlock `json:"network_status,omitempty"` | ||
| // NetworkOrder lists network names in the order they were connected | ||
| // to the container (earliest first). Used by pesto to pick which | ||
| // container IP to forward traffic to: the first-connected network wins. | ||
| // Populated by the caller alongside NetworkStatus. | ||
| NetworkOrder []string `json:"network_order,omitempty"` |
There was a problem hiding this comment.
I think the struct as is is send to netavark via json, via do however do not need or want to send this.
My suggestion would be add json:"-" to both and add a comment that this is not part fo the netavark jsone format.
c7b86fe to
e65c0d0
Compare
Fixes: podman-container-tools/podman#28771 Signed-off-by: Jan Rodák <hony.com@seznam.cz>
…ate tracking Fixes: podman-container-tools/podman#28769 Signed-off-by: Jan Rodák <hony.com@seznam.cz>
e65c0d0 to
18c09ba
Compare
| MapGuestAddrIpv4 = mapGuestAddrIpv4 | ||
| MapGuestAddrIpv6 = mapGuestAddrIpv6 | ||
| GatewayIpv6 = gatewayIpv6 | ||
| GuestAddrIpv6 = guestAddrIpv6 |
There was a problem hiding this comment.
I suppose you might as well just export the variable without a second reassignment but that is just a minor style thing not worth blocking on.
|
LGTM |
portMappingsToPestoArgsnow generates-t hostIP/hostPort:containerIP/containerPort--map-guest-addrfor forwardingPestoAddPorts/PestoDeletePortsfrom netavarkSetup/TeardownFixes: podman-container-tools/podman#28769
Fixes: podman-container-tools/podman#28770
Fixes: podman-container-tools/podman#28771